package HierarchyPackage.AffixHierarchies;

import HierarchyPackage.*;
import ClassifiedDataSetPackage.ClassifiedDataSet;
import ClassifiedDataSetPackage.ClassifiedDataSetStorage;
import DataSetPackage.SimpleView;
import DataSetPackage.View;
import OrderedCoverPackage.SC;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import org.osdtsystem.utils.MemoryStorage;

/**
 *
 * @author Martin Haulrich
 */
public class PrefixHierarchy extends AffixHierarchy {

    /**
     * Constructor
     */
    public PrefixHierarchy() {
        super();
        setTop(new PrefixHClass(this, ""));
    }

    public synchronized PrefixHClass getHClass(String suf) {
        return new PrefixHClass(this, suf);
    }

    @Override
    public ClassifiedDataSet getClassifications(Collection<Integer> dids, View v, HClass topClass,
            int maxDepth, int minCount, int cutFraction, boolean saveData) {
        /**
         * @TODO minCount currently not in use
         */
        /**
         * @TODO saveData currently not in use
         */
        ClassifiedDataSetStorage result = new ClassifiedDataSetStorage(new MemoryStorage(), minCount, cutFraction);

        if ((topClass instanceof PrefixHClass) && (v instanceof SimpleView)) {

            PrefixHClass tC = (PrefixHClass) topClass;
            SimpleView dv = (SimpleView) v;

            Runnable r = new PrefixThread(dids, dv, tC, maxDepth, result);
            Thread t = new Thread(r);
            t.start();

            try {
                t.join();
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }

        }
        return result;
    }

    public ClassifiedDataSetStorage getClassifications(Collection<Integer> dids, View v, HClass topClass, int maxDepth, ExecutorService pool) {

        throw new UnsupportedOperationException("Threaded is no longer supported becuase we do not have the split operation any more - simply implemt slit on the collection");
//        ClassifiedDataSetStorage result = new ClassifiedDataSetStorage();
//
//        if ((topClass instanceof PrefixHClass) && (v instanceof SimpleView)) {
//
//            PrefixHClass tC = (PrefixHClass) topClass;
//            SimpleView dv = (SimpleView) v;
//
//            int maxThreads = 16;
//            DataIDs[] dd = dids.Split(maxThreads);
//
//            Future[] ff = new Future[maxThreads];
//            Set<Integer> running = new HashSet<Integer>();
//
//            for (int i = 0; i < maxThreads; i++) {
//
//                ff[i] = pool.submit(new PrefixThread(dd[i], dv, tC, maxDepth, result));
//                running.add(i);
//            }
//
//            while (!running.isEmpty()) {
//                try {
//                    Thread.sleep(100);
//                } catch (InterruptedException ex) {
//                    ex.printStackTrace();
//                }
//                for (int i = 0; i < maxThreads; i++) {
//                    if (ff[i].isDone()) {
//                        running.remove(i);
//                    }
//                }
//            }
//
//        }
//        return result;
    }

    public ClassifiedDataSet getClassifications(Collection<Integer> dids, View dv, HClass topClass, int maxDepth, int minCount, int cutFraction, boolean saveData, SC parentClasses, int startdim) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    /**
     * Tread-object to use for threaded classification.
     */
    private class PrefixThread implements Runnable {

        /**
         * IDs for the data that this thread classifies.
         */
        Collection<Integer> dids;

        /**
         * View on the dataset.
         */
        SimpleView dv;

        /**
         * Class to classify with respect to.
         */
        PrefixHClass topClass;

        /**
         * Maximun depth of classifications.
         */
        int maxDepth;

        /**
         * Result of the classification.
         * This object is passed as a reference so all threads are writing to the same ClassifiedDataSetStorage.
         */
        ClassifiedDataSetStorage result;

        /**
         * Constructor.
         * 
         * @param dids IDs for the data that this thread classifies.
         * @param dv View on the dataset.
         * @param topClass View on the dataset.
         * @param maxDepth Maximun depth of classifications.
         * @param result Result of the classification.
         * This object is passed as a reference so all threads are writing to the same ClasClassifiedDataSetMem
         */
        public PrefixThread(Collection<Integer> dids, SimpleView dv, PrefixHClass topClass, int maxDepth, ClassifiedDataSetStorage result) {
            this.dids = dids;
            this.dv = dv;
            this.topClass = topClass;
            this.maxDepth = maxDepth;
            this.result = result;
        }

        /**
         * Initiates the classification.
         * The method that is run when the thread is started.
         */
        @Override
        public void run() {

            String topPrefix = topClass.getID();
            Map<HClass, List<Integer>> classification = new HashMap<HClass, List<Integer>>();
            Map<HClass, Integer> depts = new HashMap<HClass, Integer>();
            for (Iterator<Integer> it = dids.iterator(); it.hasNext();) {

                Integer did = it.next();
                String fea;

                fea = dv.getFeature(did);

                // Feature used here
                String prefix = fea;

                String curPre = "";

                int indexB = topPrefix.length();

                // Is prefix in fact classified under topSuffix
                if (prefix.startsWith(topPrefix)) {
                    for (int d = 0; (d <= maxDepth) && (d <= prefix.length() - topPrefix.length()); d++) {

                        curPre = topPrefix + prefix.subSequence(indexB, indexB + d);
                        PrefixHClass clas = getHClass(curPre);
                        List<Integer> classifiedByThisClass = classification.get(clas);
                        if (classifiedByThisClass == null) {
                            classifiedByThisClass = new ArrayList<Integer>();
                            classification.put(clas, classifiedByThisClass);
                        }
                        classifiedByThisClass.add(did);
                        depts.put(clas, d);
//                            result.addClassification(clas, d);
//                            result.addInstance(clas, did);
                    }
                }

            }
            for (Entry<HClass, List<Integer>> e : classification.entrySet()) {
//                result.addClassification(e.getKey(), depts.get(e.getKey()));
//                result.addInstances(e.getKey(), new HashSet<Long>(e.getValue()));
                result.addClassificationWithData(e.getKey(), depts.get(e.getKey()), e.getValue());
            }
        }
    }

    @Override
    public boolean superclassOf(HClass c1, HClass c2) {


        if (!((c1 instanceof PrefixHClass) && (c2 instanceof SuffixHClass))) {
            return false;
        }

        PrefixHClass sc1 = (PrefixHClass) c1;
        PrefixHClass sc2 = (PrefixHClass) c2;


        if (sc2.getID().endsWith(sc1.getID()) && (sc2.getID().length() > sc1.getID().length())) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * Get intersection between two classes in the hierarchy.
     * @param c1 First class in intersection.
     * @param c2 Second class in intersection.
     * @return Intersection of <code>c1</code> and <code>c2</code>.
     */
    @Override
    public HClass getIntersectionLocal(HClass c1, HClass c2) {


        String s1 = ((AffixHClass) c1).getID();
        String s2 = ((AffixHClass) c2).getID();


        // s2 is superclass to s1 in the hierarchy
        if (s1.endsWith(s2)) {
            System.err.println("c1");
            return c1;
        } else {
            // s1 is superclass to s2 in the hierarchy
            if (s2.endsWith(s1)) {
                System.err.println("c2");
                return c2;
            } else {
                System.err.println("null");
                return null;
            }
        }

    }

    @Override
    public boolean isClassifiedBy(Integer did, View dv, HClass classification) {

        if (dv instanceof SimpleView) {
            if (classification instanceof PrefixHClass) {

                SimpleView sdv = (SimpleView) dv;
                PrefixHClass sclas = (PrefixHClass) classification;

                String fea = sdv.getFeature(did);
                String topPre = sclas.getID();

                if (fea.endsWith(topPre) && (fea.length() > topPre.length())) {
                    return true;
                } else {
                    return false;
                }

            }
        }
        return false;
    }

    @Override
    public Map<HClass, Integer> saveEst(String filename, Set<HClass> classes) throws IOException {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public Map<Integer, HClass> loadEst(String filename, Set<Integer> classIDs) throws IOException {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public HClass newClass(Integer did, View v) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public String getType() {
        return "PrefixHierachy";
    }
}
